//create by wang zhong, 2012-08-02 09:28:59

#include "fileops.h"
#include "OSThread.h"
#ifdef __Win32__
#include <direct.h>
#include <io.h>
#define sm_snprintf _snprintf
#else
#define sm_snprintf snprintf
#endif
#include  <fcntl.h>


Bool16 FileOps::Exists(const char* pfilepath)
{
    struct stat data;

    int err = stat(pfilepath, &data);

    if (err == -1)
        return false;
#ifdef __Win32__
    return ((data.st_mode & S_IFREG ) == S_IFREG  );
#else
    return S_ISREG(data.st_mode) || S_ISCHR(data.st_mode) || S_ISBLK(data.st_mode) || S_ISFIFO(data.st_mode);
#endif
}

Bool16 FileOps::DirExist(const char* path)
{
    struct stat data;

    int err = stat(path, &data);

    if (err == -1)
        return false;

#ifdef WIN32
    return ((data.st_mode & S_IFDIR) == S_IFDIR);
#else
    return S_ISDIR(data.st_mode);
#endif
}

Bool16 FileOps::Stat(const char* pfilepath, struct stat &statBuf)
{
    Bool16 result = true;
    if (0 != stat(pfilepath,&statBuf) ) // something wrong
    {
        if ( OSThread::GetErrno() == ENOENT )   // doesn't exist
            result = false;
    }

    return result;
}

OS_Error FileOps::RecursiveMakeDir(char * pInPath)
{
	char inPath[256] = {0};


	if (pInPath == NULL)return OS_NoErr;

	strcpy(inPath, pInPath);

    //iterate through the path, replacing '/' with '\0' as we go
    char *thePathTraverser = inPath;

    //skip over the first / in the path.
    if (*thePathTraverser == kPathDelimiterChar)
        thePathTraverser++;

    while (*thePathTraverser != '\0')
    {
        if (*thePathTraverser == kPathDelimiterChar)
        {
            //we've found a filename divider. Now that we have a complete
            //filename, see if this partial path exists.

            //make the partial path into a C string
            *thePathTraverser = '\0';
            OS_Error theErr = MakeDir(inPath);
            //there is a directory here. Just continue in our traversal
            *thePathTraverser = kPathDelimiterChar;

            if (theErr != OS_NoErr)
                return theErr;
        }
        thePathTraverser++;
    }

    //need to create the last directory in the path
    return MakeDir(inPath);
}

OS_Error FileOps::MakeDir(char * pInPath)
{
	char inPath[256] = { 0 };


	if (pInPath == NULL)return OS_NoErr;

	strcpy(inPath, pInPath);

    struct stat theStatBuffer;
    if (::stat(inPath, &theStatBuffer) == -1)
    {
        //this directory doesn't exist, so let's try to create it
#ifdef __Win32__
        if (::mkdir(inPath) == -1)
#else
        if (::mkdir(inPath, S_IRWXU) == -1)
#endif
            return (OS_Error)OSThread::GetErrno();
    }
#ifdef __Win32__
    else if (!(theStatBuffer.st_mode & _S_IFDIR)) // MSVC++ doesn't define the S_ISDIR macro
        return EEXIST; // there is a file at this point in the path!
#else
    else if (!S_ISDIR(theStatBuffer.st_mode))
        return EEXIST;//there is a file at this point in the path!
#endif

                      //directory exists
    return OS_NoErr;
}

FileOps::FileOps():m_nFileFd(-1),m_u64Length(0),m_s64ModTime(0),m_bIsDir(false),m_bReadOnly(true)
{
    memset(m_strFilepath, 0, sizeof(m_strFilepath));
}

FileOps::FileOps(const char* pfilepath, Bool16 readOnly):m_nFileFd(-1),m_u64Length(0), m_s64ModTime(0),m_bIsDir(false)
{
    Set(pfilepath, readOnly); 
}

void FileOps::Open(const char* pfilepath, Bool16 readOnly)
{
	if (-1 != m_nFileFd)return;

	m_nFileFd = -1;
	m_s64ModTime = 0;
	m_u64Length = 0;

	int flag = (readOnly) ? O_RDONLY : O_RDWR;
#if __Win32__
	flag |= O_BINARY;
#elif __linux__
	flag |= O_LARGEFILE;
#endif

	if (!readOnly && !Exists(pfilepath))
	{
		flag |= O_CREAT;
		m_nFileFd = open(pfilepath, flag, S_IREAD | S_IWRITE
#ifdef __linux__
			| S_IRGRP
#endif
			);
	}
	else
		m_nFileFd = open(pfilepath, flag);
	//printf("%s, m_nFileFd  = %d\r\n", pfilepath, m_nFileFd);

	if (m_nFileFd != -1)
	{
		struct stat buf;
		::memset(&buf, sizeof(buf), 0);
		if (::fstat(m_nFileFd, &buf) >= 0)
		{
			m_u64Length = buf.st_size;
			m_s64ModTime = (SInt64)buf.st_mtime * 1000;
			if (m_s64ModTime < 0)
				m_s64ModTime = 0;
#ifdef __Win32__
			m_bIsDir = buf.st_mode & _S_IFDIR;
#else
			m_bIsDir = S_ISDIR(buf.st_mode);
#endif
			m_bReadOnly = readOnly;
			sm_snprintf(m_strFilepath, sizeof(m_strFilepath) - 1, pfilepath);
		}
		else
			Close();
	}

	return;
}




void FileOps::Set(const char* pfilepath, Bool16 readOnly, Bool16 truncated)
{
    Close();

    int flag = (readOnly) ? O_RDONLY : O_RDWR;
#if __Win32__
    flag |= O_BINARY;
#elif __linux__
    flag |= O_LARGEFILE;
#endif
    if(!readOnly && truncated)
        flag |= O_TRUNC;

    if (!readOnly && !Exists(pfilepath))
    {
        flag |= O_CREAT;
        m_nFileFd = open(pfilepath, flag, S_IREAD| S_IWRITE
#ifdef __linux__
            |S_IRGRP
#endif
        );
    }
    else
        m_nFileFd = open(pfilepath, flag);
    //printf("%s, m_nFileFd  = %d\r\n", pfilepath, m_nFileFd);

    if (m_nFileFd != -1)
    {
        struct stat buf;
        ::memset(&buf,0,sizeof(buf));
        if (::fstat(m_nFileFd, &buf) >= 0)
        {
            m_u64Length = buf.st_size;
            m_s64ModTime = (SInt64)buf.st_mtime * 1000;
            if (m_s64ModTime < 0)
                m_s64ModTime = 0;
#ifdef __Win32__
            m_bIsDir = buf.st_mode & _S_IFDIR;
#else
            m_bIsDir = S_ISDIR(buf.st_mode);
#endif
            m_bReadOnly = readOnly;
            sm_snprintf(m_strFilepath, sizeof(m_strFilepath) - 1, pfilepath);
        }
        else
            Close();
    }   
}

void FileOps::Set(const char* pfilepath, Bool16 readOnly)
{
    Close();

    int flag =  (readOnly)?O_RDONLY:O_RDWR;
#if __Win32__
    flag |= O_BINARY;
#elif __linux__
    flag |= O_LARGEFILE;
#endif

    if (!readOnly && !Exists(pfilepath))
    {
        flag |= O_CREAT;
        m_nFileFd = open(pfilepath, flag, S_IREAD| S_IWRITE
#ifdef __linux__
            |S_IRGRP
#endif
        );
    }
    else
        m_nFileFd = open(pfilepath, flag);
    //printf("%s, m_nFileFd  = %d\r\n", pfilepath, m_nFileFd);

    if (m_nFileFd != -1)
    {
        struct stat buf;
        ::memset(&buf,sizeof(buf),0);
        if (::fstat(m_nFileFd, &buf) >= 0)
        {
            m_u64Length = buf.st_size;
            m_s64ModTime = (SInt64)buf.st_mtime * 1000;
            if (m_s64ModTime < 0)
                m_s64ModTime = 0;
#ifdef __Win32__
            m_bIsDir = buf.st_mode & _S_IFDIR;
#else
            m_bIsDir = S_ISDIR(buf.st_mode);
#endif
            m_bReadOnly = readOnly;
            sm_snprintf(m_strFilepath, sizeof(m_strFilepath) - 1, pfilepath);
        }
        else
            Close();
    }

	return;
}

void FileOps::Close()
{
    if (m_nFileFd != -1)
       ::close(m_nFileFd);

    m_nFileFd = -1;
    m_s64ModTime = 0;
    m_u64Length = 0;
}

OS_Error FileOps::Read(UInt64 inPosition, void* inBuffer, SInt32 inLength, SInt32& outRcvLen)
{
    if(!IsOpened())
        return EMFILE;

#if __Win32__
    if (_lseeki64(m_nFileFd, inPosition, SEEK_SET) == -1)
#else
    if (lseek(m_nFileFd, inPosition, SEEK_SET) == -1)
#endif
        return OSThread::GetErrno();

    outRcvLen = ::read(m_nFileFd, (char*)inBuffer, inLength);
    if (outRcvLen == -1)
        return OSThread::GetErrno();

    return OS_NoErr;
}

OS_Error FileOps::Write(UInt64 inPosition, void* inBuffer, SInt32 inLength, SInt32& outRcvLen)
{
    if(!IsOpened() || m_bReadOnly)
        return EMFILE;

#if __Win32__
    if (_lseeki64(m_nFileFd, inPosition, SEEK_SET) == -1)
#else
    if (lseek(m_nFileFd, inPosition, SEEK_SET) == -1)
#endif
        return OSThread::GetErrno();

    outRcvLen = ::write(m_nFileFd, (char*)inBuffer, inLength);
    if (outRcvLen == -1)
        return OSThread::GetErrno();

    return OS_NoErr;
}

OS_Error FileOps::DelFile(const char * fileName)
{
	Close();
	
	int nRet = ::remove(fileName);

	return nRet;
}
